import math
import random
import time
import sys
#import Gnuplot

from numpy.core.numeric import zeros

# Constantes
N = 10
VENTAJA = 2
SALIDA = 9
ENTRADA = 0

#Clase.
class Proceso:
        def __init__(self,matriz,epsilon):
                """Constructor de Clase"""
                self.matriz = matriz
                self.v = self.inic_v()
                self.ve = []
                self.vacum = self.inic_vacum()
                self.macum = self.inic_macum()
                self.paso = 0
                self.hits = []
                self.esq = -1
                self.esq_ant = -1
                self.vueltas = []
                self.victorias = 0
                self.epsilon = epsilon
                self.historia_ve = [[],[],[],[],[],[],[],[],[],[]]

        def inic_v(self):
                v = []
                for i in range(N):
                        v.append( 1 / float(N) )
                return v

        def inic_vacum(self):
                vacum = []
                vacum.append(self.v[0])
                for i in range(N):
                        vacum.append(vacum[i] + self.v[i])
                return vacum
       
        def inic_macum(self):
                macum = zeros((N,N))
                for j in range(N):
                        macum[0][j] = self.matriz[0][j];
                        for i in range(1,N):
                                macum[i][j] = (self.matriz[i][j] + macum[i-1][j]);
                return macum
                       
        def primer_paso(self):
                self.esq = ENTRADA
                self.paso = 1
                return
               
        # Dado un paso, retorna el proximo.
        def sig_dado_ant(self):
                self.esq_ant = self.esq
                r = random.random()
                i = 0
                self.paso += 1

                while (r > self.macum[i][self.esq] and i+1<N):
                        i += 1
                self.esq = i

                return
                       
        def guardar_historia_ve(self,v):
                for i in range(N):
                        self.historia_ve[i].append(v[i])
       
        # Calcula el vector estacionario ve
        def calcular_ve(self):
                print "Calculando vector estacionario"
                self.primer_paso() # estado/esquina inicial
                self.hits = zeros(N) # cant de veces (hits) que pase por cada esquina
                v = zeros(N) # vector a retornar
                v_ant = zeros(N)
                min_cant_pasos = 100
               
                while ((self.paso < min_cant_pasos) or (not self.converge_v(v,v_ant))):
                        self.paso += 1
                        v_ant[self.esq] = v[self.esq]
                        v[self.esq] = self.hits[self.esq] / self.paso
                        self.hits[self.esq] += 1
                        self.sig_dado_ant()
                        self.guardar_historia_ve(v)
                self.ve = v
                return v
               
        # Todos los valores del vector actual converge?
        def converge_v(self, actual, anterior):
                for i in range(N):
                        if (abs(actual[i]-anterior[i]) > self.epsilon):
                                return False
                return True
               
        def converge(self,actual,anterior):
                if ( abs(actual-anterior) > self.epsilon ):
                        return False
                else:
                        return True
               
        def media_tvuelta(self):
       
                min_vueltas = 100

                media = 0.0
                media_ant = -1
                while ( not self.converge(media,media_ant) ):

                        self.primer_paso()
                        while ( self.esq != SALIDA ):
                                self.sig_dado_ant()

                        self.vueltas.append(self.paso)
                        media_ant = media
                        media = sum(self.vueltas) / float(len(self.vueltas))
                       
                return media
       
        # Muestra datos de la clase            
        def print_datos(self):
       
                #print "Vector V"
                #print self.v

                #print "Vector Acumulado"
                #print self.vacum
                return
       
class Escape:

        def __init__(self):
                self.muestras = 10
                self.vdrno = zeros( (self.muestras,2) )
                self.vbond = zeros( (self.muestras,2) )
               
                self.pasostotalesdr=0
                self.pasostotalesbond=0
                self.hitsdr = 0.0
                self.hitsbond = 0.0
                self.salidabond = 0.0
                self.salidasbond = []

        def guardar_datos(self,p1,p2,i):
                self.vdrno[i][0] = p1.paso
                self.pasostotalesdr += p1.paso
                self.vdrno[i][1] = p1.esq

                self.vbond[i][0] = p2.paso
                self.pasostotalesbond += p2.paso
                self.vbond[i][1] = p2.esq
                return
               
        def gano_p1(self,p1,p2):
                return ( p1.esq == ENTRADA and p1.esq_ant == SALIDA )
               
        def gano_p2(self,p1,p2):
                return ( p1.esq == p2.esq )
       
        def mover(self,p1,p2):
                """ Simula un juego hasta que uno gane """

                p1.primer_paso()
                for p in range(VENTAJA):
                        p1.sig_dado_ant()

                p2.primer_paso()
                # hago pasos mientras que el proceso 1 no gane y el proceso 2 no gane
                # el proceso 1 gana si va del 9 al 0
                # el proceso 2 gana si el proceso 2 se encuentra en el mismo casillero que el proceso 1
                while ( not self.gano_p1(p1,p2) and not self.gano_p2(p1,p2) ):
                        p1.sig_dado_ant()
                        p2.sig_dado_ant()

                """ quien gano? """
                if ( self.gano_p1(p1,p2) ): #gano p1
                        p1.victorias += 1
                        return 1
                elif ( self.gano_p2(p1,p2) ): # gano p2
                        p2.victorias += 1
                        return 2
               
        def simular(self, p1, p2):
               
                # probabilidad de ganar de p1 (drno escapa)
                print "\nSimulado escape...."
                p_ganar1 = -1
                p_ganar2 = -1
                p_ganar1_ant = -3
                ganador = 0
                i = 0
                min_cant = 100
               
                while ( i < min_cant or not(p1.converge(p_ganar1, p_ganar1_ant)) ):


                        self.mover(p1,p2)
                        #self.guardar_datos(p1,p2,i)                    
                        i += 1
                       
                        p_ganar1_ant = p_ganar1
                        p_ganar1 = p1.victorias / float(i)
                        p_ganar2 = p2.victorias / float(i)

                print "P(gana drno) ",p_ganar1
                print "P(gana james) ",p_ganar2
                print "suma:",p_ganar1 + p_ganar2

                #print self.vdrno, self.vbond
                #print "hits dr", self.hitsdr," dr p: ", self.hitsdr / self.muestras , " hits bond", self.hitsbond," bond p: ", self.hitsbond / self.muestras
                #print "v drno", len(self.vdrno)
               
                return i
       
        def correlacion_cruzada(self,p1,p2):
       
                print "\nCorrelacion Cruzada en estado Estacionario: "
                correlacion = 0.0
               
                if ( len(p1.ve) == 0 and len(p2.ve) == 0 ):     #si NO fueron calculados
                        p1.calcular_ve()
                        p2.calcular_ve()
               
                for i in range(N):
                        for j in range(N):
                                correlacion += i*j*p1.ve[i]*p2.ve[j]

                return correlacion
               
                       
# Matrices (se podrian cargar desde archivos)
drno_mat = [
        [0,0.15,0,0,0,0,0,0,0,0.7],
        [1.0,0,0.3,0,0.3,0,0,0,0,0],
        [0,0.15,0,1.0,0,0,0,0,0,0],
        [0,0,0.7,0,0,0,0,0,0,0],
        [0,0.7,0,0,0,0,0.15,0,0,0],
        [0,0,0,0,0,0,0.15,0,0,0],
        [0,0,0,0,0.7,1.0,0,0.15,0,0],
        [0,0,0,0,0,0,0.7,0,1.0,0.3],
        [0,0,0,0,0,0,0,0.15,0,0],
        [0,0,0,0,0,0,0,0.7,0,0],
]

bond_mat = [
        [0,0.33,0,0,0,0,0,0,0,0,0.5],
        [1,0,0.5,0,0.5,0,0,0,0,0],
        [0,0.33,0,1,0,0,0,0,0,0],
        [0,0,0.5,0,0,0,0,0,0,0],
        [0,0.33,0,0,0,0,0.33,0,0,0],
        [0,0,0,0,0,0,0.33,0,0,0],
        [0,0,0,0,0.5,1,0,0.33,0,0],
        [0,0,0,0,0,0,0.33,0,1,0.5],
        [0,0,0,0,0,0,0,0.33,0,0],
        [0,0,0,0,0,0,0,0.33,0,0],
]



if (len(sys.argv)==1):
        print "Puede si desea darle valor a el epsilon como prametro"
        
        drno = Proceso(drno_mat,0.0001)
        bond = Proceso(bond_mat,0.0001)
elif(len(sys.argv) > 1):
        drno = Proceso(drno_mat,sys.argv[1])
        bond = Proceso(bond_mat,sys.argv[1])
       

print "\nVector estacionario de drno"
print drno.calcular_ve()


print "\nVector estacionario de bond"
print bond.calcular_ve()

skape = Escape()
skape.simular(drno,bond)

print "Tiempo medio de llegar a la salida del drno: ",drno.media_tvuelta()
print "Tiempo medio de llegar a la salida de james: ",bond.media_tvuelta()

print skape.correlacion_cruzada(drno,bond)

print "Presione una tecla para continuar..."
nombre = raw_input()

def graficar_ve(p1):
        gp = Gnuplot.Gnuplot(persist = 1)
        gp('set data style lines')
        plots = []
        for i in range(N):
                plot = Gnuplot.PlotItems.Data(p1.historia_ve[i], title="Esquina i")
                plots.append(plot)
        gp.plot(*plots)
        return

#graficar_ve(drno)
